<?php
/**
 * 阿里云 操作类
 * User: 我想有个家
 * Date: 2019/1/16
 * Time: 22:11
 */

namespace App\Service;


use AlibabaCloud\Client\AlibabaCloud;
use AlibabaCloud\Client\Exception\ClientException;
use AlibabaCloud\Client\Exception\ServerException;
use App\Log;
use Hyperf\Contract\ConfigInterface;

class AliService {
    private $accessKey;
    private $accessSecret;
    private $roleArn;
    private $bucket;
    private $endpoint;
    private $liveKey;
    private $push;
    private $play;

    public function __construct(ConfigInterface $config) {
        $config = $config->get('md.ali');
        $this->accessKey = $config['accessKey'];
        $this->accessSecret = $config['accessSecret'];
        $this->roleArn = $config['roleArn'];
        $this->bucket = $config['bucket'];
        $this->endpoint = $config['endpoint'];
        $this->liveKey = $config['liveKey'];
        $this->push = $config['push'];
        $this->play = $config['play'];
    }

    /**
     * 发送短信验证码
     * @param $mobile string 手机号码
     * @param $signName string 签名
     * @param $templateCdoe string 短信模板ID
     * @param $param array 模板参数
     * @return string 空表示成功，否则返回错误信息
     */
    public function sendSms($mobile, $signName, $templateCdoe, $param) {
        try {
            AlibabaCloud::accessKeyClient($this->accessKey, $this->accessSecret)
                ->regionId('cn-hangzhou')
                ->asDefaultClient();
            $result = AlibabaCloud::rpc()
                ->product('Dysmsapi')
                ->scheme('https')// https | http
                ->version('2017-05-25')
                ->action('SendSms')
                ->method('POST')
                ->options([
                    'query' => [
                        'PhoneNumbers' => $mobile,
                        'SignName' => $signName,
                        'TemplateCode' => $templateCdoe,
                        'TemplateParam' => json_encode($param),
                    ],
                ])
                ->request();
            $result = $result->toArray();
            if ($result['Code'] == 'OK') {
                return '';
            } else {
                $this->log($result['Message'], $result);
                return $result['Message'];
            }
        } catch (ClientException $e) {
            $this->log("{$e->getErrorCode()} {$e->getErrorMessage()}");
            return $e->getErrorMessage();
        } catch (ServerException $e) {
            $this->log("{$e->getErrorCode()} {$e->getErrorMessage()}");
            return $e->getErrorMessage();
        }
    }

    /**
     * 日志记录
     * @param string $msg
     * @param array $data
     */
    private function log(string $msg, array $data = []) {
        Log::get()->critical($msg, $data);
    }

    /**
     * 获取推流地址
     * @param $streamName string 流名称
     * @return string
     */
    public function pushUrl($streamName) {
        $time = 60;
        $url = $this->getUrl("{$streamName}", $time);
        return "{$this->push}{$url}";
    }

    /**
     * 获取播流地址
     * @param $streamName string 流名称
     * @return string
     */
    public function playUrl($streamName) {
        $time = 60;
        $url = $this->getUrl("{$streamName}.m3u8", $time);
        return "{$this->play}{$url}";
    }

    /**
     * 获取uri  包含鉴权串(10分钟有效期)
     * @param $uri string uri地址 （示例：/lb/aa  或者 /lb/aa.flv）
     * @param $time int 有效期（秒，示例：10分钟有效 600）
     * @return string
     */
    private function getUrl($uri, $time) {
        $uri = "/dc/$uri";
        $timestamp = time() + $time;
        $uuid = $this->uuid();
        $md5hash = md5("{$uri}-{$timestamp}-{$uuid}-0-{$this->liveKey}");
        return "{$uri}?auth_key={$timestamp}-{$uuid}-0-{$md5hash}";
    }

    /**
     * 获取uuid
     * @return string
     */
    private function uuid() {
        return sprintf('%04x%04x%04x%04x%04x%04x%04x%04x',

            // 32 bits for "time_low"
            mt_rand(0, 0xffff), mt_rand(0, 0xffff),

            // 16 bits for "time_mid"
            mt_rand(0, 0xffff),

            // 16 bits for "time_hi_and_version",
            // four most significant bits holds version number 4
            mt_rand(0, 0x0fff) | 0x4000,

            // 16 bits, 8 bits for "clk_seq_hi_res",
            // 8 bits for "clk_seq_low",
            // two most significant bits holds zero and one for variant DCE1.1
            mt_rand(0, 0x3fff) | 0x8000,

            // 48 bits for "node"
            mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
        );
    }


    /**
     * 阿里云OSS STS方式上传KEY
     * 上传失效 15分钟
     * @return array
     */
    public function getSts() {
        $tokenExpire = '900';
        $policy = json_encode([
            "Statement" => [
                [
                    "Action" => [
                        "oss:GetObject",
                        "oss:PutObject",
                        "oss:DeleteObject",
                        "oss:ListParts",
                        "oss:AbortMultipartUpload",
                        "oss:ListObjects"
                    ],
                    "Effect" => "Allow",
                    "Resource" => ["acs:oss:*:*:{$this->bucket}/*", "acs:oss:*:*:{$this->bucket}"]
                ]
            ],
            "Version" => "1"
        ]);

        try {
            AlibabaCloud::accessKeyClient($this->accessKey, $this->accessSecret)
                ->regionId('cn-hangzhou')
                ->asDefaultClient();
            $result = AlibabaCloud::rpc()
                ->product('Sts')
                ->scheme('https')// https | http
                ->version('2015-04-01')
                ->action('AssumeRole')
                ->method('POST')
                ->options([
                    'query' => [
                        'RoleSessionName' => "client_name",
                        'RoleArn' => $this->roleArn,
                        'DurationSeconds' => $tokenExpire,
                        'Policy' => $policy,
                    ],
                ])
                ->request();
            return [
                'code' => 200,
                'data' => $result->toArray(),
            ];
        } catch (ClientException $e) {
            return [
                'code' => 500,
                'msg' => $e->getErrorMessage(),
            ];
        } catch (ServerException $e) {
            return [
                'code' => 500,
                'msg' => $e->getErrorMessage(),
            ];
        }
    }

    /**
     * 获取上传key
     * @param $dir string 上传目录
     * @return array
     */
    public function getPolicy($dir) {
        $id = $this->accessKey;          // 请填写您的AccessKeyId。
        $key = $this->accessSecret;     // 请填写您的AccessKeySecret。
        // $host的格式为 bucketname.endpoint，请替换为您的真实信息。
        $host = "https://{$this->bucket}.{$this->endpoint}";
        // $dir = 'bdw/';          // 用户上传文件时指定的前缀。
        // $callbackUrl为上传回调服务器的URL，请将下面的IP和Port配置为您自己的真实URL信息。
        // $callbackUrl = 'http://88.88.88.88:8888/aliyun-oss-appserver-php/php/callback.php';

        // $callback_param = array('callbackUrl'=>$callbackUrl,
        //     'callbackBody'=>'filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}',
        //     'callbackBodyType'=>"application/x-www-form-urlencoded");
        // $callback_string = json_encode($callback_param);

        // $base64_callback_body = base64_encode($callback_string);
        $now = time();
        $expire = 60;  //设置该policy超时时间是10s. 即这个policy过了这个有效时间，将不能访问。
        $end = $now + $expire;
        try {
            $expiration = $this->gmt_iso8601($end);
        } catch (\Exception $e) {
            return [];
        }
        //最大文件大小.用户可以自己设置
        $condition = array(0 => 'content-length-range', 1 => 0, 2 => 1048576000);
        $conditions[] = $condition;

        // 表示用户上传的数据，必须是以$dir开始，不然上传会失败，这一步不是必须项，只是为了安全起见，防止用户通过policy上传到别人的目录。
        $start = array(0 => 'starts-with', 1 => '$key', 2 => $dir);
        $conditions[] = $start;
        $arr = array('expiration' => $expiration, 'conditions' => $conditions);
        $policy = json_encode($arr);
        $base64_policy = base64_encode($policy);
        $string_to_sign = $base64_policy;
        $signature = base64_encode(hash_hmac('sha1', $string_to_sign, $key, true));
        $response = array();
        $response['accessid'] = $id;
        $response['host'] = $host;
        $response['policy'] = $base64_policy;
        $response['signature'] = $signature;
        $response['expire'] = $end;
        // $response['callback'] = $base64_callback_body;
        $response['dir'] = $dir;  // 这个参数是设置用户上传文件时指定的前缀。
        return $response;
    }

    /**
     * web直传前时间处理
     * @param $time
     * @return string
     * @throws \Exception
     */
    private function gmt_iso8601($time) {
        $dtStr = date("c", $time);
        $mydatetime = new \DateTime($dtStr);
        $expiration = $mydatetime->format(\DateTime::ISO8601);
        $pos = strpos($expiration, '+');
        $expiration = substr($expiration, 0, $pos);
        return $expiration . "Z";
    }
}
